【X68000(Z)アセンブラ講座 第027回 半透明機能】   こんにちは!! 暑いですね。 皆さんは大丈夫ですか? 僕はここ1週間で2回ほど異常なほど大量の汗をかいて熱中症寸前になりました(^^; Facebookで仲良くなった方(天才Winプログラマー(!))から聞いた話ですが 暑くて具合が悪い時は高麗人参の入ったエナジードリンクを飲むと良いとの事で 昨日早速試してみたら身体的にも精神的にもシャキッとして快適に過ごせました☆ 辛い時は皆さんも試してみて下さい(^^) 今回は画面の半透明機能についてです。 半透明機能はWinPCの3D機能(Direct3DやOpenGL)にもありますが 半透明機能を実装したのはX68000の方が先です☆ サンプルプログラムはテキスト画面と65536色グラフィクス画面での半透明を行います。 サンプルプログラムを動かす前に 512×512ピクセル8ビットカラー(256色)のBMP画像ファイルを用意して 'BM_TEST9.bmp'と言うファイル名で保存して下さい。 破線の内側を'A_BLEND.s'と言うファイル名で保存して下さい。 ------------------------------------------------------------------------------------------------ ********************************************************************************* * * アプリ名 : A_BLEND.x * * アルファブレンド(半透明機能) * * Ver1.00 * ******************************************************************************** .cpu 68000 * CPUのタイプ include A:\XC\INCLUDE\DOSCALL.MAC * OSコール用マクロの読み込み include A:\XC\INCLUDE\IOCSCALL.MAC * IOCS(BIOS)コール用マクロの読み込み * システム領域のアドレス cg_palette equ $E82000 * CG用パレットデータの先頭アドレス sp_palette equ $E82200 * テキストとスプライトのパレットデータ先頭アドレス cg_buffer equ $C00000 * CG-VRAMの先頭アドレス cg0_scroll_x equ $E80018 * CG画面0のスクロールX座標 cg0_scroll_y equ $E8001A * CG画面0のスクロールY座標 cg1_scroll_x equ $E8001C * CG画面1のスクロールX座標 cg1_scroll_y equ $E8001E * CG画面1のスクロールY座標 vec_vdisp equ $000118 * 垂直同期(V-DISP)割り込み vec_hsync equ $00013C * 水平同期(H-SYNC)割り込み video_r1 equ $E82500 * 画面プライオリティ video_r2 equ $E82600 * 半透明/特殊プライオリティ制御 .data * '.data'以降のデータはデータセクションに配置される file00: dc.b 'BM_TEST9.bmp',0 * 読み込むBMPのファイル名 msg_return: dc.b 13,10,0 * 改行コード('13'+'10') & 終端コード('0') msg01: dc.b 'BMP File Name : ',0 msg02: dc.b 'File Open Error !',13,10,0 msg03: dc.b 'BMP形式のファイルではありません !',13,10,0 msg04: dc.b '8ビットカラー(256色)の画像ではありません !',13,10,0 msg05: dc.b 'CGのサイズが512×512ピクセルではありません !',13,10,0 .even * '.even'以降の偶数アドレスにデータを置く fh: dc.w 0 * ファイルハンドル(ファイル管理番号) viewW: dc.l 0 viewH: dc.l 0 bmp_header_start: * bmp_header_endまでBMPファイルのヘッダーデータ領域 BMFH: * BITMAPFILEHEADER ビットマップファイルヘッダー 14bytes bmph: dc.b 0, 0 * 'B'と'M'ならばBMP形式画像 dc.l 0 * BMPファイルのサイズ dc.w 0 * 予約領域1 dc.w 0 * 予約領域2 dc.l 0 * ファイルヘッダの先頭からビットマップの先頭までの長さ BMIH: * BITMAPINFOHEADER ビットマップインフォヘッダー 40bytes biSize: dc.l 0 * BITMAPINFOHEADER領域のバイト数 biWidth: dc.w 0, 0 * 横ピクセル数 biHeight: dc.w 0, 0 * 縦ピクセル数 biPlanes: dc.w 0 * 1 biBitCount: dc.w 0 * カラーモード(4/8/16/24/32) biCompression: dc.l 0 * BI_RGB biSizeImage: dc.l 0 * イメージの全バイト数 biXPelsPerMeter:dc.l 0 * 水平解像度 biYPelsPerMeter:dc.l 0 * 垂直解像度 biClrUsed: dc.w 0, 0 * 使用されている色の数 biClrImportant: dc.l 0 * 0 bmp_header_end: * BMPファイルのヘッダー領域の終わり .bss * '.bss'以降データバッファ BMPal ds.b 1024 * パレットデータバッファ(4*256byte確保する) bmp: ds.b 262144 * ピクセルデータバッファ(256*256byte確保する) cgpal: ds.b 512 * パレットデータバッファ(2*256byte確保する) .text * '.text'以降のプログラムはテキストセクションに配置される start: * スタートアドレス * ユーザーモードからスーパーバイザーモードに移行する moveq.l #_B_SUPER,d0 * d0にIOCS(BIOS)機能番号を代入 move.l #0,d1 * 数値0をd1に代入 movea.l d1,a1 * d1をa1にコピー trap #15 * IOCSコール実行 * 画面モードの設定 moveq.l #_CRTMOD,d0 * 画面モードの設定 move.w #12,d1 * 画面モードの番号 : 16 = 768x512x4bit / 12 = 512x512x16bit trap #15 * 画面をクリアして表示をオンにする moveq.l #_G_CLR_ON,d0 * 画面をクリアして表示をオンにする trap #15 * moveq.l #_SP_ON,d0 * trap #15 * moveq.l#_SP_INIT,d0 * trap #15 pea msg01 * 文字列データmsg01のアドレスをスタックに積む dc.w _PRINT * 文字列の表示 addq.l #4,sp * spの位置を元に戻す pea file00 * ファイル名の先頭アドレスをスタックに積む dc.w _PRINT addq.l #4,sp pea msg_return * 改行するために改行コードのアドレスをスタックに積む dc.w _PRINT addq.l #4,sp * 画面プライオリティの設定 * 半透明初期化@ movea.l #video_r1,a1 move.w (a1),d0 and.w #$ff,d0    or.w #$2400,d0 move.w d0,(a1) * 半透明の設定 * 半透明初期化A movea.l #video_r2,a2 move.w (a2),d0 and.w #$ff,d0 or.w #$1f00,d0 move.w d0,(a2) bsr load_bmp * BMPファイル読み込みルーチンの呼び出し bsr set_palette * CGパレット設定ルーチンの呼び出し move.w #$fffe,sp_palette+2 * テキストカラー1番を白に設定 bsr draw_bmp * 16ビットVRAMに一時的に8ビットカラーコードを入れる bsr to65536 * 8ビットカラーを16ビットカラーで描画する loop: bsr box * テキスト画面の真ん中に白いボックスを描画する moveq.l #_BITSNS,d0 moveq.l #0,d1 trap #15 and.b #%00000010,d0 tst.b d0 bne end bra loop end: moveq.l #_CRTMOD,d0 * 画面モードの設定 move.w #16,d1 * 画面モードの番号 : 16 = 768x512x4 trap #15 * スーパーバイザーモードからユーザーモードに戻る moveq.l #_B_SUPER,d0 move.l SP,a1 trap #15 * アプリ終了 dc.w _EXIT * OSコール実行 : _EXIT = プログラムの終了 * テキスト画面の真ん中に白いボックスを描画する box: movea.l #$E00000,a0 adda.l #128*128+16,a0 move.w #0,d7 box_1: move.l a0,a1 move.w #0,d6 box_2: move.b #$ff,(a1)+ addq.w #1,d6 cmp.w #32,d6 blt box_2 adda.l #128,a0 addq.w #1,d7 cmp.w #256,d7 blt box_1 rts * 8ビットカラーを16ビットカラーに変換して描画 to65536: lea bmp,a0 movea.l #$C00000,a2 moveq.l #0,d7 to65536_1: moveq.l #0,d6 to65536_2: lea cgpal,a1 move.b (a0)+,d0 and.l #$ff,d0 add.l d0,d0 adda.l d0,a1 move.w (a1),d0 move.w d0,(a2)+ addq.w #1,d6 cmp.w #512,d6 blt to65536_2 addq.w #1,d7 cmp.w #512,d7 blt to65536_1 rts load_bmp: * BMPファイルのデータを読み込むルーチン * File Open (ファイルアクセス開始) move.w #0,-(sp) * アクセスモードを指定。0 = 読み込みモード/1 = 書き込みモード pea file00 * ファイル名が書かれた部分の先頭アドレスをスタックに積む dc.w _OPEN * OSコールの実行 : ファイルのオープン addq.l #6,sp * SPの位置を元に戻す tst.l d0 * もしd0がプラスなら、 bpl load_bmp_0 * ラベルload_bmp_0に飛ぶ。 pea msg02 * エラーメッセージ dc.w _PRINT addq.l #4,sp dc.w _EXIT * OSコール実行 : エラーによりソフトウェアの終了 load_bmp_0: move.w d0,fh * _OPENを実行した後にd0にファイルハンドルが入っているので * d0のファイル管理番号をfhに保存する * BMFH read move.l #14,-(sp) * 読み込みバイト数をスタックに積む pea BMFH * アドレスBMFHをスタックに積む move.w fh,-(sp) * ファイルハンドルをスタックに積む dc.w _READ * OSコール実行 : ファイルの読み込み add.l #10,sp * SPの位置を元に戻す cmp.w #'BM',bmph * BMPファイルヘッダーが'BM'か比較する beq load_bmp_1 * 'BM'ならload_bmp_1に飛ぶ pea msg03 dc.w _PRINT addq.l #4,sp dc.w _EXIT * 'BM'以外なのでアプリ終了 load_bmp_1: * BMIH read move.l #40,-(sp) * 40byteのファイルヘッダー pea BMIH move.w fh,-(sp) dc.w _READ * ファイル読み込みのOSコールを実行する lea 10(sp),sp move.w biBitCount,d0 ror.w #8,d0 * d0.wの中身の2進数データを右に8ビットローテート(Intel対策) cmp.w #8,d0 * 8Bitカラー(256色)か比較する beq load_bmp_2 pea msg04 dc.w _PRINT addq.l #4,sp dc.w _EXIT load_bmp_2: * BMP パレットの読み込み move.l #1024,-(sp) * パレットデータのサイズをスタックに積む pea BMPal move.w fh,-(sp) dc.w _READ lea 10(sp),sp * BMP ピクセルデータの読み込み move.l #262144,-(sp) * ピクセルデータの全てのサイズをスタックに積む pea bmp * bmp = 読み込んだピクセルデータを配置するアドレス move.w fh,-(sp) dc.w _READ lea 10(sp),sp * File Close (ファイルアクセス終了) move.w fh,-(sp) dc.w _CLOSE addq.l #2,sp rts * サブルーチンの最後に配置(呼出元にリターンし次の命令から実行再開) * RGBQUAD { Blue8, Green8, Red8, Reserved8 } = %RRRRRRRR_GGGGGGGG_BBBBBBBB_AAAAAAAA * palette { Green5, Red5, Blue5, Alpha1 } = %GGGGGRRR_RRBBBBB0 * Set CG Palette set_palette: movea.l #cg_palette,a0 * CG用パレットレジスターアドレス lea BMPal,a1 * パレットデータバッファアドレス lea cgpal,a2 moveq.l #0,d7 set_palette_1: move.b (a1),d0 * a1が示すアドレスからd0にバイトデータを読み込む * ↓32ビット色の青色成分8ビットのうち上位5ビットを抜き取り16ビット色の青色成分として使う lsr.w #2,d0 * 青色ビットデータの位置を16ビット色内の青色位置に右シフト and.w #%0000000000111110,d0 * 16ビット色の青色成分以外のビットを0にする move.b 1(a1),d1 * a1に1を足したアドレスからd1にバイトデータを読み込む * ↓32ビット色の緑色成分8ビットのうち上位5ビットを抜き取り16ビット色の緑色成分として使う lsl.w #8,d1 * 緑色ビットデータの位置を16ビット色内の緑色位置に左シフト and.w #%1111100000000000,d1 * 16ビット色の緑色成分以外のビットを0にする move.b 2(a1),d2 * a1に2を足したアドレスからd2にバイトデータを読み込む * ↓32ビット色の赤色成分8ビットのうち上位5ビットを抜き取り16ビット色の赤色成分として使う lsl.w #3,d2 * 赤色ビットデータの位置を16ビット色内の赤色位置に左シフト and.w #%0000011111000000,d2 * 16ビット色の赤色成分以外のビットを0にする * 青赤緑のそれぞれの色成分を論理和命令で一つの16ビットカラーに完成させる or.w d1,d0 or.w d2,d0 or.w #1,d0 * 完成した色データをパレットメモリーに書き込んで更新する * move.w d0,(a0)+ * 16ビットデータを書き込んだ後にa0に2を足す move.w d0,(a2)+ * 次の変換元色データを取り出す前にa1のアドレスを更新しておく addq.l #4,a1 addq.w #1,d7 * 色番号を次の番号に更新する cmp.w #256,d7 * 現在の色番号と16を比較する blt set_palette_1 * 現在の色番号が16未満ならアドレスset_palette_1に飛ぶ rts * 呼出元に戻る * Draw BMP draw_bmp: * in d0.w = X座標 * in d1.w = Y座標 movem.l d0/d1/d5-d7/a1/a5/a6,-(sp) move.l #$C00000,a6 * CG-Vram先頭アドレスをa6に代入する ext.l d1 * d1をロングに拡張 moveq #10,d5 * 次の命令でd5を使うため10を入れて準備する lsl.l d5,d1 * d1の値を左に10ビットシフトして1024倍する ext.l d0 * d0をロングに拡張 add.l d0,d0 * 1ピクセルあたり2バイト(16ビット)なのでd0を2倍する add.l d1,d0 * d0にd1を足す add.l d0,a6 * a6にd0を足して描画先頭アドレスの完成 lea bmp,a1 * a1にピクセルデータの先頭アドレスを代入する move.l #512-1,d7 * d7にループ回数から1を引いた値を代入する draw_bmp_1: move.l #512-1,d6 * d6にループ回数から1を引いた値を代入する movea.l a6,a5 * a6のアドレス値をa5にコピーする draw_bmp_2: move.b (a1)+,d5 * アドレスa1のピクセルデータd5に読み込む move.w d5,(a5)+ * アドレスa5内のVRAMアドレスにd5のピクセルを置く dblt d6,draw_bmp_2 add.l #512*2,a6 * 次のY座標を処理するためにa6に横VRAMサイズ1024を足す dblt d7,draw_bmp_1 movem.l (sp)+,d0/d1/d5-d7/a1/a5/a6 rts * 呼出元に戻る * Emd Of File ------------------------------------------------------------------------------------------------ コマンドプロンプトから、 A>as A_BLEND.s [Enter] A>lk A_BLEND.o [Enter] A>A_BLEND.x [Enter] のように入力すると 半透明の映像が表示されます。 ESCキーで終了します。 [ プログラムの解説 ] 今回のサンプルの何処が65536色(16ビットカラー)なのかというと VRAMのカラーモードが65536色モードであり 用意して頂いた8ビットカラーの画像を 'to65536'ルーチンの中で8ビット画像を65536色モード用に変換して描画表示しています。 半透明機能の使用に関しては 「半透明初期化@」と「半透明初期化A」を実行すれば良いだけなので この2つはそのままご自分のプログラムにコピーして使っても構いませんし 最適化/カスタマイズして使っても構いません。 お疲れ様でした(^^) それではまた!!